package crypto;

import java.security.SecureRandom;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.lang3.ArrayUtils;

import com.lambdaworks.codec.Base64;
import com.lambdaworks.crypto.SCrypt;

import constants.EncryptStandards;

public class AESEncrypt  {
	// private static byte[] keyValue;
	private static Cipher encC;
	private static Cipher decC;
	private static SecretKeySpec key;

	public static void initialize (String userPasswdStr) throws Exception {
		long startTime = System.nanoTime();
		byte[] userPasswd = userPasswdStr.getBytes("UTF-8");
		byte[] salt = {-1, 6, 18}; //TODO load salt from database users table
		byte[] keyValue = SCrypt.scrypt(userPasswd, salt, EncryptStandards.N, EncryptStandards.r,
				EncryptStandards.p, EncryptStandards.dkLen);
		key = generateKey(keyValue);
		encC = Cipher.getInstance(EncryptStandards.KEY_ALGO);
		decC = Cipher.getInstance(EncryptStandards.KEY_ALGO);
		long endTime = System.nanoTime();
		System.out.println("AESEncrypt initialized in "+ ((double)( endTime - startTime))/1000000000 +" seconds .");
	}

	public static byte[] encrypt(byte[] data) throws Exception {
		byte[] iv = SecureRandom.getSeed(EncryptStandards.ivLen);
		encC.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
		byte[] encVal = encC.doFinal(data);	
		byte[] encrypted = ArrayUtils.addAll(iv, encVal);
		return encrypted ;
	}
	
	public static byte[] decryptRaw (byte[] rawdata) throws Exception {
		byte[] iv = ArrayUtils.subarray(rawdata, 0, EncryptStandards.ivLen);
		byte[] data = ArrayUtils.subarray(rawdata,  EncryptStandards.ivLen, rawdata.length);
		return decrypt(data, iv);
	}
	
	public static byte[] decrypt (byte[] data, byte[] iv) throws Exception {
		decC.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
		byte[] decrypted = decC.doFinal(data);	
		return decrypted ;
	}

	private static SecretKeySpec generateKey(byte[] keyValue) throws Exception {
		SecretKeySpec key = new SecretKeySpec(keyValue, EncryptStandards.ALGO);
		return key;
	}
}
